﻿using System.Diagnostics;
using System.IO;

using LuaInterface;

namespace System.Lua
{
    /// <summary>
    /// Reads and executes Lua code and exposes the resulting Lua runtime.
    /// </summary>
    public sealed class LuaReader : IDisposable
    {
        private LuaInterface.Lua luaRuntime;

        /// <summary>
        /// Initializes a new instance of the <see cref="LuaReader"/> class.
        /// </summary>
        /// <param name="chunk">The chunk to execute.</param>
        private LuaReader(string chunk)
        {
            if (chunk == null)
                throw new ArgumentNullException("chunk");

            // Initialize Lua runtime and execute the file
            luaRuntime = new LuaInterface.Lua();

            try
            {
                Trace.TraceInformation("Interpreting Lua chunk...");

                luaRuntime.DoString(chunk);

                Trace.TraceInformation("Lua chunk interpreted.");
            }
            catch (LuaException exception)
            {
                Trace.TraceError("Could not interpret Lua chunk: " + exception.Message);
                Trace.TraceInformation("Chunk: " + chunk);

                throw new CouldNotInterpretException(Strings.CouldNotParseMessage, exception);
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LuaReader"/> class.
        /// </summary>
        /// <param name="textReader">The text reader.</param>
        private LuaReader(TextReader textReader)
            : this(textReader.ReadToEnd())
        {
        }

        /// <summary>
        /// Gets a value indicating whether the variable is defined.
        /// </summary>
        /// <param name="variableName">The name of the variable.</param>
        /// <returns><c>True</c>, if the variable is defined; <c>false</c> otherwise.</returns>
        public bool IsVariableDefined(string variableName)
        {
            if (variableName == null)
                throw new ArgumentNullException("variableName");

            return this[variableName] != null;
        }

        /// <summary>
        /// Gets the number of variables defined.
        /// </summary>
        public int VariableCount
        {
            get { return luaRuntime.GetTable("_G").Keys.Count - 1; /* Deduct "_G" itself */ }
        }

        /// <summary>
        /// Gets the value of a variable.
        /// </summary>
        /// <param name="fullPath">The fully path to the variable.</param>
        /// <returns></returns>
        public object this[string fullPath]
        {
            get { return luaRuntime[fullPath]; }
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (luaRuntime != null)
                {
                    luaRuntime.Dispose();
                    luaRuntime = null;
                }
            }
        }

        /// <summary>
        /// Creates a new <see cref="LuaReader"/>.
        /// </summary>
        public static LuaReader Create()
        {
            return new LuaReader(String.Empty);
        }

        /// <summary>
        /// Creates a new <see cref="LuaReader"/>.
        /// </summary>
        /// <param name="chunk">The path to the Lua file.</param>
        public static LuaReader Create(string chunk)
        {
            if (chunk == null)
                throw new ArgumentNullException("chunk");

            return new LuaReader(chunk);
        }

        /// <summary>
        /// Creates a new <see cref="LuaReader"/>.
        /// </summary>
        /// <param name="textReader">The <see cref="TextReader"/> to use.</param>
        public static LuaReader Create(TextReader textReader)
        {
            if (textReader == null)
                throw new ArgumentNullException("textReader");

            return new LuaReader(textReader);
        }
    }
}
